using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
using Microsoft.Xna.Framework.GamerServices;
using RebelStar.Engine.Graphics;
using RebelStar.Engine.Maps;
using RebelStar.Engine.Players;
using RebelStar.Engine.Players.Fog;
using RebelStar.Engine.Sprites;
using RebelStar.Engine.Units;
using RebelStar.Engine.Units.Equipments;
using RebelStar.Engine.Units.Equipments.Weapons;
using Color = Microsoft.Xna.Framework.Graphics.Color;
using Point = Microsoft.Xna.Framework.Point;
using Rectangle = Microsoft.Xna.Framework.Rectangle;

namespace RebelStar.Engine
{
    public class GameBase
    {
        readonly IGameGraphicsDevice device;
        readonly List<Player> players;
        ISurface workSurface;
        ISurface bufSurface;
        Map map;
//        RenderTarget2D previewTarget;
//        MapPreview preview;
        bool showPreview;
        Point cursor;
        Point selectionStart;
        GameMode mode;
        float scale = 1f;
        bool enableFog;
        Weapon currentWeapon;
        Action currentAction;
        Player currentPlayer;
        private MapEntry cursorEntry;
        private int cursorState;
        private Rectangle selectionRect;

        private GameBase()
        {
            this.showPreview = false;
            this.enableFog = true;
            this.mode = GameMode.Starting;
            this.players = new List<Player>();
        }
        public GameBase(IGameGraphicsDevice graphicsDevice) : this()
        {
            this.device = graphicsDevice;
            graphicsDevice.Paint += device_Paint;
            graphicsDevice.ClientSizeChanged += window_ClientSizeChanged;
            window_ClientSizeChanged(this, EventArgs.Empty);
        }

        public void Initialize()
        {
            Content.Initialize(device.ServiceProvider);
            
            workSurface = device.CreateSurface();
            bufSurface = device.CreateBuffer();

//            preview = new MapPreview(
//                map,
//                device.Viewport.Width - 200, 0,
//                200, 150);

//            if (mode == GraphicDeviceMode.DirectX && 
//                preview != null)
//                previewTarget = new RenderTarget2D(
//                    device,
//                    preview.Width, preview.Height,
//                    1,
//                    device.PresentationParameters.BackBufferFormat);
        }
        public void LoadMap()
        {
            map = Map.LoadTest();
            map.Resize(device.ClientSize);
            map.SetScale(scale);

//            Player player = new Player(map, "Player 1");
//            players.Add(player);
//            Unit unit = new Unit(map.UnitSprites[1], new Point(2, 5));
//            unit.Slots[1].Weapon = new Weapon("Pistol", 3, 0.6f, 3, 5);
//            player.Units.Add(unit);
//            unit = new Unit(map.UnitSprites[0], new Point(5,11));
//            player.Units.Add(unit);
//            unit.Slots[0].Weapon = new Weapon("Pistol", 3, 0.6f, 3, 5);
//            unit.Slots[1].Weapon = new Weapon("Rifle", 7, 0.9f, 5, 7);
//
//            player = new Player(map, "Player 2");
//            players.Add(player);
//            unit = new Unit(map.UnitSprites[1], new Point(7, 3));
//            unit.Slots[1].Weapon = new Weapon("Pistol", 3, 0.6f, 3, 5);
//            player.Units.Add(unit);
//
//            foreach (Player pl in players)
//                pl.RefreshFog();
//
//            currentPlayer = players[0];
        }
        public void ReDraw()
        {
            device.Redraw();
        }
        public void Scroll(int xofs, int yofs)
        {
            map.Scroll(xofs, yofs);
            this.ReDraw();
        }
        public void BlendMap(Color color)
        {
            MapEntry.BlendColor = color;
            this.ReDraw();
        }
        public void SetCursor(int px, int py)
        {
            cursor = map.CalcScreenToMap(new Point(px, py));

            if (cursor.X >= map.Width || cursor.Y >= map.Height ||
                cursor.X < 0 || cursor.Y < 0)
                if (cursorEntry != null)
                    cursorEntry = null;

            MapEntry newsel = map[cursor];
            if (newsel != cursorEntry)
                cursorEntry = newsel;

            if (selectionStart != Point.Zero)
                this.select(new Rectangle(
                               selectionStart.X,
                               selectionStart.Y,
                               cursor.X - selectionStart.X + 1,
                               cursor.Y - selectionStart.Y + 1));

            if (this.fireMode &&
                currentPlayer.SelectedUnit != null)
                this.cursorState = traceVisible(currentPlayer.SelectedUnit.Position, cursor) ? 2 : 0;

            device.Redraw();
        }
        public void StartSelection(int x, int y)
        {
            if (this.mode == GameMode.Select)
                selectionStart = new Point(x, y);
        }
        public void EndSelection(int x, int y)
        {
            switch (mode)
            {
                case GameMode.Starting:
                    break;
                case GameMode.Select:
                    if (selectionStart != Point.Zero)
                    {
                        Rectangle rect = new Rectangle(
                                       selectionStart.X, selectionStart.Y,
                                       x - selectionStart.X + 1, y - selectionStart.Y + 1);
                        this.select(rect);
                    }
                    currentPlayer.SelectUnit(currentPlayer.GetUnit(new Point(x, y)));
                    selectionStart = Point.Zero;
                    break;
                case GameMode.Fire:
                    Unit unit = currentPlayer.SelectedUnit;
                    if (unit != null)
                        if (traceVisible(unit.Position, new Point(x, y)))
                        {
                            unit.Fire(currentWeapon, currentAction);
                            foreach (Player player in players)
                            {
                                Unit target = player.GetUnit(new Point(x, y));
                                if (target != null)
                                {
                                    float chance = currentWeapon.Accuracy*
                                                   (1 - calculateAccurencyLoss(unit.Position, target.Position));
                                    Random r = new Random((int) DateTime.Now.Ticks);
                                    if (r.NextDouble() <= chance)
                                        target.Hit(currentWeapon.Power);
                                }
                            }

                            this.fireMode = false;
                            this.ReDraw();
                        }
                    break;
            }
        }
        public void ResetSelection()
        {
            if (this.fireMode) this.fireMode = false;
            else
            {
                selectionStart = Point.Zero;
                this.select(Rectangle.Empty);
                if (currentPlayer!=null)
                    currentPlayer.SelectUnit(null);
            }
        }
        public void NextTurn()
        {
            currentPlayer.ResetParameters();
            changePlayer();
        }
        public void KeyPress(Keys keyCode)
        {
            Point pos = Point.Zero;
            if (currentPlayer.SelectedUnit != null)
                pos = currentPlayer.SelectedUnit.Position;
            currentPlayer.KeyPress(keyCode);
            if (currentPlayer.SelectedUnit != null &&
                currentPlayer.SelectedUnit.Position != pos)
                this.select(currentPlayer.SelectedUnit.Position);
            switch (keyCode)
            {
                case Keys.Space:
                    this.setFireMode(currentPlayer.SelectedUnit);
                    break;
            }
        }
        public void SetScale(float scale)
        {
            this.scale = scale;
            map.SetScale(this.scale);
            this.ReDraw();
        }
        public void SetFireMode(Unit unit, Weapon weapon, Action action)
        {
            if (!unit.CanFire(weapon, action)) return;

            this.fireMode = true;
            this.currentWeapon = weapon;
            this.currentAction = action;
        }
        public void AddPlayer(Gamer gamer)
        {
            this.AddPlayer(gamer.GetProfile().Motto);
        }
        public void AddPlayer(string name)
        {
            this.players.Add(new Player(this.map, name));
            if (mode == GameMode.Starting)
            {
                currentPlayer = this.players[0];
                currentPlayer.RefreshFog();
                mode = GameMode.Select;
            }
        }
        public void ChangeTerrain(MapEntry entry, TerrainSprite terr, int state)
        {
            entry.ChangeTerrain(terr, state);
            map.Invalidate();
        }
        public void AddStructure(MapEntry entry, StructureSprite struc, int state)
        {
            entry.AddStructure(struc, state);
            map.Invalidate();
        }

        public int MapWidth
        {
            get
            {
                return map.Width;
            }
        }
        public int MapHeight
        {
            get
            {
                return map.Height;
            }
        }
        public Rectangle MapVisibleRect
        {
            get { return map.VisibleRange; }
        }
        public bool ShowPreview
        {
            get { return showPreview /*&& preview != null*/; }
            set { showPreview = value; }
        }
        public Point Cursor
        {
            get { return cursor; }
        }
        public Player CurrentPlayer
        {
            get { return currentPlayer; }
            set { currentPlayer = value; }
        }
        public IEnumerable<Player> Players
        {
            [DebuggerStepThrough]
            get { return players.AsReadOnly(); }
        }
        public bool EnableFog
        {
            get { return enableFog; }
            set
            {
                enableFog = value;
                this.ReDraw();
            }
        }
        public float Scale
        {
            get { return scale; }
        }
        public Map Map
        {
            get { return map; }
        }
        public MapEntry CursorEntry
        {
            get { return map[this.cursor]; }
        }

        bool fireMode
        {
            get { return this.mode == GameMode.Fire; }
            set
            {
                if (value)
                {
                    this.mode = GameMode.Fire;
                    this.cursorState = 2;
                }
                else
                {
                    this.mode = GameMode.Select;
                    this.cursorState = 0;
                }
            }
        }

        public IEnumerable Selection
        {
            get { return map.GetSelection(selectionRect); }
        }

        void draw(Player player)
        {
            if (workSurface.BeginDraw())
            {
                //            if (showPreview && preview != null)
                //                drawPreview();

                try
                {
                    if (map.Invalidated)
                    {
                        device.Clear(System.Drawing.Color.CornflowerBlue);
                        map.Draw(bufSurface, enableFog ? player.Fog : null);
                    }
                    workSurface.Copy(bufSurface);

                    //                if (showPreview && preview != null)
                    //                    workSurface.DrawSprite(
                    //                        previewTarget.GetTexture(),
                    //                        new Rectangle(preview.X, preview.Y, preview.Width, preview.Height),
                    //                        Color.White);

                    map.DrawSprite(map.Selection, cursorState, workSurface, cursor.X, cursor.Y, scale);
//                    if (selectionRect != Rectangle.Empty &&
//                        selectionRect.Contains(j, i))
//                        this[j, i].DrawSprite(selection, 1, surface, c.X, c.Y, scale);
                    foreach (MapEntry entry in this.Selection)
                        map.DrawSprite(this.map.Selection, 1, workSurface, map.GetPosition(entry).X, map.GetPosition(entry).Y, scale);


                    foreach (Unit unit in player.Units)
                        map.DrawSprite(unit.Sprite, unit.CurrentState,
                                       workSurface,
                                       unit.Position.X, unit.Position.Y,
                                       this.scale);
                    foreach (Player pl in players)
                        foreach (Unit unit in pl.Units)
                            if (!enableFog || player.Fog[map[unit.Position]].Status == FogStatus.Visible)
                                map.DrawSprite(unit.Sprite, unit.CurrentState,
                                               workSurface,
                                               unit.Position.X, unit.Position.Y,
                                               this.scale);
                }
                finally
                {
                    workSurface.EndDraw();
                    device.Present();
                }
            }
        }
//        void drawPreview()
//        {
//            if (preview.Changed)
//            {
//                device.SetRenderTarget(0, previewTarget);
//                workSurface.Begin(SpriteBlendMode.AlphaBlend);
//                try
//                {
//                    preview.DrawSprite(workSurface);
//                }
//                finally
//                {
//                    workSurface.End();
//                    device.SetRenderTarget(0, null);
//                }
//            }
//        }
        bool traceVisible(Point p1, Point p2)
        {
            int power = 100;

            IList<MapEntry> path = map.GetPath(p1, p2);
            for (int i = 0; i < path.Count-1; i++)
            {
                power -= 100-path[i].Transparency;
                if (power <= 0) return false;
            }
            return true;
        }
        float calculateAccurencyLoss(Point p1, Point p2)
        {
            float loss = 0;

            IList<MapEntry> path = map.GetPath(p1, p2);
            for (int i = 0; i < path.Count-1; i++)
                loss += (100-path[i].Transparency)/100f;
            
            return loss;
        }
        void changePlayer()
        {
            int i = players.IndexOf(currentPlayer);
            Player player;
            do
            {
                player = players[(i == players.Count - 1) ? 0 : ++i];
            } 
            while (!player.IsAlive);
            currentPlayer = player;
        }
        void select(Point point)
        {
            this.select(new Rectangle(point.X, point.Y, 1, 1));
        }
        void select(Rectangle rect)
        {
            selectionRect = rect;

            if (rect.Width <= 0)
                selectionRect = new Rectangle(
                    selectionRect.Right - 1, selectionRect.Top,
                    -selectionRect.Width + 2, selectionRect.Height);
            if (rect.Height <= 0)
                selectionRect = new Rectangle(
                    selectionRect.Left, selectionRect.Bottom - 1,
                    selectionRect.Width, -selectionRect.Height + 2);

            this.device.Redraw();
        }
        void setFireMode(Unit unit)
        {
            if (unit != null &&
                unit.DefaultSlot != null && unit.DefaultSlot.Weapon != null &&
                unit.DefaultSlot.Weapon.DefaultAction != null)
                this.SetFireMode(unit, unit.DefaultSlot.Weapon, unit.DefaultSlot.Weapon.DefaultAction);
        }

        void window_ClientSizeChanged(object sender, EventArgs e)
        {
            if (map != null)
            {
                bufSurface = device.CreateBuffer();
                map.Resize(device.ClientSize);
                this.draw(currentPlayer);
            }
        }
        void device_Paint(object sender, EventArgs e)
        {
            switch (mode)
            {
                case GameMode.Starting:
                    device.Clear(System.Drawing.Color.CornflowerBlue);
                    break;
                case GameMode.Select:
                case GameMode.Fire:
                    if (currentPlayer != null)
                        this.draw(currentPlayer);
                    break;
            }
        }
    }
}